#ifndef SERIAL_H
#define SERIAL_H

#include <deque>
#include <boost/thread.hpp>

typedef boost::recursive_mutex::scoped_lock scoped_lock;
typedef boost::function<void(void)> void_function;

class SerialCommunication
{
public:
  virtual ~SerialCommunication();

  /** Puts another byte in the send-queue.
    @param p_cByte the actual byte to be send
    @return always "1" (atm)*/
  int sendByte(unsigned char p_cByte);

  /** Returns the number of bytes in the Send-Queue.
    @return byte-count in send-queue.*/
  inline int getSendQueueLength() const
  {
    return m_dOut.size();
  }

protected:
  SerialCommunication(long p_lBaud);

  /** This function is called when some bytes were
    read from the interface.*/
  virtual void handleReceivedBytes() = 0;

  /** This pure virtual must be implemented in order to catch error-states.
    @param p_iCode any number used for detailed information
    @param p_sError a pointer to a string with detailed informations*/
  virtual void handleError(int p_iCode, char const *p_sError) = 0;

  /** Opens the connection and creates a Read- and a Write-Thread.
    @return "1" on success, "-1" on error*/
  int openConnection();

  /** Informs all child-threads about upcoming dooms-day. After waiting for
    those threads, it closes any open connection on the serial device.
    @return always "1" (atm)*/
  int closeConnection();

  /** Returns the number of bytes available in the Receive-Queue.
    @return byte-count in receive-queue.*/
  inline int getBytesAvailable() const
  {
    return m_dIn.size();
  }
  /** Returns and removes the first byte from the Receive-Queue.
    @param p_pByte pointer to the address, where the byte should be written
    @return 1 on success, 0 else*/
  int getByte(unsigned char *p_pByte);

  /** Returns the first byte from the Receive-Queue.
    @param p_pByte pointer to the address, where the byte should be written
    @return 1 on success, 0 else*/
  int peekByte(unsigned char *p_pByte);

  bool setDevice(char const *p_sDevice);

private:
  void writeThread(void);
  void readThread(void);

  bool m_bWantRun;
  int  m_iFD;
  long m_lBaud;

  char *m_pDevice;
  char *m_pError;

  std::deque<unsigned char> m_dOut;
  std::deque<unsigned char> m_dIn;

  boost::thread *m_ptReceive;
  boost::thread *m_ptSend;

  boost::recursive_mutex m_syncIn;
  boost::recursive_mutex m_syncOut;

  boost::recursive_mutex m_syncRead;
  boost::recursive_mutex m_syncWrite;

  boost::recursive_mutex m_syncAccess;

  boost::condition m_cReceiveReady;
  boost::condition m_cSendReady;
};

#endif // SERIAL_H
